﻿#include "command_manager.hh"
#include "include/json/json.h"
#include "kconfig/interface/config.h"
#include "../box/service_box.hh"
#include "../detail/lang_impl.hh"
#include "../util/string_util.hh"
#include "box_config_impl.hh"
#include "command_impl.hh"
#include "http_base_impl.hh"

kratos::service::CommandManager::CommandManager(service::ServiceBox *box) {
  box_ = box;
}

kratos::service::CommandManager::~CommandManager() {
  for (auto it : command_map_) {
    // 解除关联
    it.second->detach();
  }
}

auto kratos::service::CommandManager::start() -> bool {
  if (!load_config()) {
    return false;
  }
  if (box_) {
    box_->write_log(
      lang::LangID::LANG_STARTUP_INFO_COMMAND_MANAGER,
      klogger::Logger::INFORMATION,
      enable_ ? "yes" : "no"
    );
  }
  if (enable_) {
    if (box_) {
      box_->write_log(
        lang::LangID::LANG_STARTUP_INFO_COMMAND_MANAGER_ADDRESS,
        klogger::Logger::INFORMATION,
        listen_address_.c_str()
      );
    }
    http_ = make_unique_pool_ptr<http::HttpBaseImpl>(box_);
    if (!http_->start()) {
      return false;
    }
    return wait_request();
  } else {
    return true;
  }
}

auto kratos::service::CommandManager::stop() -> void {
  if (http_) {
    http_->stop();
    http_.reset();
  }
}

auto kratos::service::CommandManager::update(std::time_t ms) -> void {
  for (auto& it : command_map_) {
    it.second->update(ms);
  }
  for (auto& it : path_map_) {
    it.second->update(ms);
  }
  if (http_) {
    http_->update(ms);
  }
}

auto kratos::service::CommandManager::add_command(const std::string &name,
                                                  CommandImpl *command)
    -> bool {
  if (command_map_.find(name) != command_map_.end()) {
    return false;
  }
  command_map_[name] = command;
  return true;
}

auto kratos::service::CommandManager::add_path(const std::string& path,
  CommandImpl* command) -> bool {
  if (path_map_.find(path) != path_map_.end()) {
      return false;
  }
  path_map_[path] = command;
  return true;
}

auto kratos::service::CommandManager::del_command(const std::string &name)
    -> void {
  auto it = command_map_.find(name);
  if (it == command_map_.end()) {
    return;
  }
  command_map_.erase(it);
}

auto kratos::service::CommandManager::del_path(const std::string& path) -> void {
  auto it = path_map_.find(path);
  if (it == path_map_.end()) {
      return;
  }
  path_map_.erase(it);
}

auto kratos::service::CommandManager::get_box() -> ServiceBox * { return box_; }

auto kratos::service::CommandManager::is_enable() -> bool {
  return (enable_ && !host_.empty() && (port_ != 0));
}

auto kratos::service::CommandManager::get_host() -> const std::string & {
  return host_;
}

auto kratos::service::CommandManager::get_port() -> int { return port_; }

auto kratos::service::CommandManager::load_config() -> bool {
  if (!box_) {
    return true;
  }
  auto *config = box_->get_config().get_config_ptr();
  if (!config) {
    return false;
  }
  if (config->has("command.enable")) {
    // 默认为开启状态
    if (config->string("command.enable")->get() == "false") {
      enable_ = false;
    }
  }
  if (config->has("command.listen")) {
    const auto &value = config->string("command.listen")->get();
    if (!util::get_host_config(value, host_, port_)) {
      return false;
    }
    listen_address_ = value;
  }
  return true;
}

auto kratos::service::CommandManager::wait_request() -> bool {
  return http_->wait_request_async(
      host_, port_, 0,
      [&](http::HttpCallPtr request, http::HttpCallPtr response, std::uint64_t) -> bool {
        if (request.expired() || response.expired()) {
          // 外部关闭
          return true;
        }
        std::string command;
        auto command_proc = check_request(request, response, command);
        if (!command_proc) {
          // 外部关闭
          return true;
        }
        if (box_) {
          if (request.lock()->get_uri().empty()) {
            box_->write_log(lang::LangID::LANG_COMMAND_INFO,
              klogger::Logger::INFORMATION,
              request.lock()->get_content().c_str());
          } else {
            box_->write_log(lang::LangID::LANG_COMMAND_INFO,
              klogger::Logger::INFORMATION,
              request.lock()->get_uri().c_str());
          }
        }
        auto result = command_proc->on_command(request, command,
                                               request.lock()->get_content());
        if (!result) {
          response.lock()->set_content("{\"error\" : \"In progress\"}");
          // 外部关闭
          return true;
        }
        // 未完成
        return false;
      });
}

auto kratos::service::CommandManager::check_request(http::HttpCallPtr request,
                                                    http::HttpCallPtr response,
                                                    std::string &command)
    -> kratos::service::CommandImpl * {
  auto locked_req = request.lock();
  auto locked_resp = response.lock();
  Json::CharReaderBuilder builder;
  std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
  Json::Value root;

  auto it = path_map_.find(locked_req->get_uri());
  if (it != path_map_.end()) {
    command = locked_req->get_uri();
    return it->second;
  } else {
    auto content = locked_req->get_content();
    if (!content.empty()) {
      const auto* start = content.c_str();
      const auto* end = content.c_str() + content.size();
      Json::String error;
      if (!reader->parse(start, end, &root, &error)) {
        locked_resp->set_content("{\"error\" : \"" + error + "\"}");
        return nullptr;
      } else {
        command = root["command"].asString();
        auto it = command_map_.find(command);
        if (it == command_map_.end()) {
          locked_resp->set_content("{\"error\" : \"Command not found\"}");
          return nullptr;
        }
        return it->second;
      }
    } else {
      locked_resp->set_content("{\"error\" : \"Invalid json format\"}");
      return nullptr;
    }
  }
}
